<html>
  <head>
    <title>Displaying RDF with Protovis</title>
    <script type="text/javascript" src="protovis-3.2.js"></script>
    <script type="text/javascript" src="data.js"></script>
    <style type="text/css">

body {
  margin: 0;
}

#lovely {
  border: 3px solid #A9C2A1;
  width: 600px;
  height: 400px;
}

.code_snippet {
  border: 1px solid black;
  margin: 1em;
  margin-left: 2em;
  margin-right: 20%;
  padding: 2px;
}


    </style>
  </head>
  <body>

<h1>Displaying RDF with Protovis</h1>
<h3>A subset of UMBEL Abstract Concepts rendered with Force-Directed
Layout</h3>

This image is an SVG graphic created by Protovis and animated in realtime
in your browser. It displays a subset of the UMBEL Abstract Concepts

(See
<a href="http://www.umbel.org/sc_ne.html">Upper Mapping and Binding
Exchange Layer</a>.)

<p>The subset was generated by starting with the concept "Action" (marked in
green in the image) and then finding all concepts related to that concept
by the predicate "narrowerTransitive" and repeating with those concepts.

<p>As you can see, there is a rich structure to the scheme of meaning
encoded in the RDF triples.

<center>
<div id="lovely">
    <script type="text/javascript+protovis">

var w = 600, h = 400,
    colors = pv.Colors.category19();

var vis = new pv.Panel()
    .width(w)
    .height(h)
    .fillStyle("white")
    .event("mousedown", pv.Behavior.pan())
    .event("mousewheel", pv.Behavior.zoom());

var force = vis.add(pv.Layout.Force)
    .nodes(graph_data.nodes)
    .links(graph_data.links);

force.link.add(pv.Line);

force.node.add(pv.Dot)
    .size(function(d) (d.linkDegree + 4) * Math.pow(this.scale, -1.5))
    .fillStyle(function(d) d.fix ? "brown" :
                    d.nodeName=="http://umbel.org/umbel/ac/Action" ?
                    "green" : colors(d.group))
    .strokeStyle(function() this.fillStyle().darker())
    .lineWidth(1)
    .title(function(d) d.nodeName)
    .event("mousedown", pv.Behavior.drag())
    .event("drag", force);

vis.render();

    </script>
</div>
</center>

This is the script that generates the Javascript 'data.js':


<div class="code_snippet">
<pre>
import os
from rdflib.graph import Graph
from rdflib import URIRef
from protovis_out import makeGraphData
from rdftools import treeSubset


# I keep a bunch of RDF files in this directory.
SEMANTIC_OORT = os.environ['SEMANTIC_OORT']

# UMBEL Abstract Concepts.
uac = os.path.join(SEMANTIC_OORT, 'umbel_abstract_concepts.n3')


# Load a triple store with the concept RDF.
UAC = Graph()
with file(uac) as f:
    UAC.parse(file=f, format='n3')

# You could also load this from the web.
# uac = 'http://www.umbel.org/ontology/umbel_abstract_concepts.n3'
# UAC.parse(uac, format='n3')


# A couple of URIRefs:
# This represents concepts that are more specialized than some other concept.
narrower = URIRef('http://www.w3.org/2004/02/skos/core#narrowerTransitive')
# This represents some "action".
action = URIRef('http://umbel.org/umbel/ac/Action')


# Query for all narrowerTransitive predicate triples.
data = list(UAC.triples((None, narrower, None)))


# Starting at Action, find the tree of all URIs related by the narrower
# predicate.
t = treeSubset(data, action, narrower)


# Print out the Javascript code for the data literal.
#print >> open('data.js', 'w'), makeGraphData(t)
print makeGraphData(t)
</pre>
</div>


This function takes care of finding all RDF triples related by a given
predicate and rooted at a particular subject.

<div class="code_snippet">
<pre>
# rdftools.py

def treeSubset(triples, subject, predicate, res=None):
    '''
    Return a set of triples that are the tree from subject and linked by
    the predicate relation.
    '''
    if res is None:
        res = set()

    # Find all objects of the subject, predicate relation.
    more = set(
        (s, p, o)
        for s, p, o in triples
        if s == subject and p == predicate
        )

    for spo in more:
        if spo not in res:
            res.add(spo)
            treeSubset(triples, spo[2], predicate, res)

    return res
</pre>
</div>

And this script contains the code to actually emit the JS version of the
data in the form accepted by the Protovis code:

<div class="code_snippet">
<pre>
# protovis_out.py


template = '''
var graph_data = {
  nodes:[
%s
  ],
  links:[
%s
  ]
};
'''


def makeNodes(a):
    return '\n'.join(
        '    {nodeName:"%s", group:1},' % (
            u' '.join(node.splitlines())[:80]
            )
        for node in a
        )


def makeLinks(g, z):
    return '\n'.join(
        '    {source:%i, target:%i, value:1},' % (z[s], z[o])
        for s, o in g
        )


def _i(pairs):
    for s, o in pairs:
        yield s; yield o


def makeGraph(pairs):
    X = sorted(set(_i(pairs))) # Get all subjects and objects.
    Z = dict((n, i) for i, n in enumerate(X)) # Map nodes to indicies.
    nodes = makeNodes(X)
    links = makeLinks(pairs, Z)
    return template % (nodes, links)


def makeGraphData(g):
    return makeGraph([(s, o) for s, p, o in g])
</pre>
</div>


  </body>
</html>

